package in;

import general.Flag;
import general.Killable;
import general.Logger;
import general.Main;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.InetAddress;
import java.net.Socket;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Scanner;

import storage.Storage;
import storage.StorageException;

/**
 * Класс, обрабатывающий соединение с клиентом
 * 
 * @author evilenka@yandex.ru
 * 
 */

public class Worker extends Thread implements Killable
{

	private Socket client;
	private Map<String, Map<String, String>> configs;
	private List<String> flags;
	private String teamIp;
	private String exploitName;

	private long ttl;
	private boolean killed = false;

	private Logger logger = Main.getLogger();

	private Storage storage;

	/**
	 * Конструктор. Принимает сокет клиента и конфиги. Запускает поток.
	 * 
	 * @param client
	 *            - сокет, с которого читаем флаги и дополнительную информацию
	 * @param configs
	 *            - конфиги
	 * @throws DaemonException
	 *             если плохой Storage
	 */

	public Worker(Socket client, Map<String, Map<String, String>> configs)
			throws DaemonException
	{
		this.client = client;
		this.configs = configs;
		try
		{
			storage = new Storage(configs);
		} catch (StorageException e)
		{
			throw new DaemonException(
					"Worker for client "
							+ client.getInetAddress().getHostAddress()
							+ " stopped because it couldn't create an instance of Storage. "
							+ e.getLocalizedMessage());
		}
		this.start();
	}

	/**
	 * Запуск потокa.
	 * 
	 */

	@Override
	public void run()
	{
		flags = new ArrayList<String>();
		// Костыль
		flags.add("");
		try
		{
			Scanner scanner = new Scanner(new BufferedReader(
					new InputStreamReader(client.getInputStream())));
			exploitName = scanner.next();
			teamIp = scanner.next();
			while (scanner.hasNext())
			{
				if (killed)
				{
					postFlagsToDB();
					try
					{
						client.close();
					} catch (IOException e)
					{
						logger
								.warn(MessageFormat
										.format(
												"Worker for client {0} couldn't close socket. {1}",
												new Object[] {
														client
																.getInetAddress()
																.getHostAddress(),
														e.getLocalizedMessage() }));
					}
					return;
				}
				String s;
				s = scanner.next();
				
				// FIXME Сделать нормально, а не костылём!
				flags.set(0, s);
				postFlagsToDB();
				/*	
				if (flags.contains(s))
				{
					continue;
				} else
				{
					flags.add(s);
				}
				 */
		}
//			postFlagsToDB();

		} catch (IOException e)
		{
			logger.warn(MessageFormat.format(
					"Worker for client {0} couldn't read from socket. {1}",
					new Object[] { client.getInetAddress().getHostAddress(),
							e.getLocalizedMessage() }));
			try
			{
				client.close();
			} catch (IOException e1)
			{
				logger.warn(MessageFormat.format(
						"Worker for client {0} couldn't close socket. {1}",
						new Object[] {
								client.getInetAddress().getHostAddress(),
								e.getLocalizedMessage() }));
			}
		}

	}

	/**
	 * Добавляет флаг в базу данных
	 */

	private void postFlagsToDB()
	{
		InetAddress ip = client.getInetAddress();
		for (String s : flags)
		{
			logger
					.info(MessageFormat
							.format(
									"Flag {0} was got from {1} by host {2} with exploit {3}",
									new Object[] { s, teamIp,
											ip.getHostAddress(), exploitName }));
			try
			{
				storage.putFlag(new Flag(s, exploitName, teamIp, ip
						.getHostAddress()));
			} catch (StorageException e)
			{
				logger.warn(MessageFormat.format(
						"Failed to post flag {0}. {1}", new Object[] { s,
								e.getLocalizedMessage() }));
			}
		}
	}

	/**
	 * Убийство.
	 * 
	 * @param milliseconds
	 *            время до принудительной остановки
	 */

	public void kill(long milliseconds)
	{
		killed = true;
		ttl = milliseconds;

	}
}